<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      html,
      body {
        margin: 0;
      }
      .container1,
      .container2 {
        display: none;
        border: 1px solid #ddd;
        margin: 20px;
      }
    </style>
  </head>

  <body>
    <div>
      <input type="file" id="oInputFile" accept="image/*" />
    </div>
    <div class="container1">
      <canvas id="myCanvas1"></canvas>
    </div>
    <div class="container2">
      <canvas id="myCanvas2"></canvas>
    </div>

    <script>
      // 文件输入框
      const oImage = document.querySelector("#oInputFile");
      // canvas1
      const oContainer1 = document.querySelector(".container1");
      const oCan1 = document.getElementById("myCanvas1");
      // 2d平面固定写法
      const ctx = oCan1.getContext("2d");

      const oContainer2 = document.querySelector(".container2");
      const oCan2 = document.getElementById("myCanvas2");
      const ctx2 = oCan2.getContext("2d");
      const opacity = 0.5;
      let img = new Image();
      // 初始位置
      let initPosition = [];
      // 截取后的图片
      let cutImage = [];
      // 获取图片后展示图片
      const showContainer = (container, canvas, width, height) => {
        container.style.width = width + "px";
        container.style.height = height + "px";
        container.style.display = "block";
        canvas.width = width;
        canvas.height = height;
      };
      const handleFileChange = (e) => {
        // 拿到上传的图片文件
        const file = e.target.files[0];
        // 读取file的宽高、url，固定写法
        const reader = new FileReader();
        // base64格式的图片url
        reader.readAsDataURL(file);
        reader.onload = (e) => {
          // 在这里拿到图片的base64格式url
          const base64 = e.target.result;
          // 赋值给img实例对象
          img.src = base64;
          img.onload = function () {
            // 这里不用箭头函数，this能拿到img的DOM元素
            const { width, height } = this;
            // 展示图片
            showContainer(oContainer1, oCan1, width, height);
            // 给canvas绘图，0,0是起始坐标
            ctx.drawImage(img, 0, 0, width, height);
            // 绘制蒙层
            ctx.fillStyle = `rgba(0,0,0,${opacity})`;
            ctx.fillRect(0, 0, width, height);
          };
        };
      };
      // 绘制截图的矩形框
      const drawCutPart = (width, height, rectWidth, rectHeight) => {
        // 仅保留现有画布内容和新形状不重叠的部分(截图框里面的内容不保存)
        ctx.globalCompositeOperation = "destination-out";
        ctx.fillStyle = "skyblue";
        ctx.fillRect(...initPosition, rectWidth, rectHeight);

        // 在现有画布的后面重新放入图片(目的是填充截图框内的图片)
        ctx.globalCompositeOperation = "destination-over";
        ctx.drawImage(img, 0, 0, width, height);
      };
      // 在canvas内鼠标移动事件
      const handleMouseMove = (e) => {
        // 获取每次移动的坐标
        const [startX, startY] = initPosition;
        // 移动后的XY
        const endX = e.offsetX;
        const endY = e.offsetY;
        // 获取截取的XY
        const [rectWidth, rectHeight] = [endX - startX, endY - startY];

        // 清空画布
        const { width, height } = oCan1;
        ctx.clearRect(0, 0, width, height);
        // 重新展示图片
        // showContainer(oContainer1, oCan1, width, height);
        // ctx.drawImage(img, 0, 0, width, height);

        // 重画mask蒙层
        ctx.fillStyle = `rgba(0,0,0,.5)`;
        ctx.fillRect(0, 0, width, height);
        // 画截图的矩形
        drawCutPart(width, height, rectWidth, rectHeight);

        // 获取截取的图片坐标和长宽
        cutImage = [startX, startY, rectWidth, rectHeight];
      };
      const drawCutImage = () => {
        const data = ctx.getImageData(...cutImage);
        showContainer(oContainer2, oCan2, cutImage[2], cutImage[3]);
        ctx2.clearRect(...cutImage);
        ctx2.putImageData(data, 0, 0);
      };
      // canvas中的松开鼠标事件
      const handleMouseUp = (e) => {
        // 得到截图的data并绘制到另一个canvas
        drawCutImage();
        // 移出事件监听
        oCan1.removeEventListener("mousemove", handleMouseMove, false);
        oCan1.removeEventListener("mouseup", handleMouseUp, false);
      };
      // canvas中的按下事件
      const handleMouseDown = (e) => {
        console.log(e.offsetX, e.offsetY);
        initPosition = [e.offsetX, e.offsetY];
        // 按下之后监听移动和松开事件
        oCan1.addEventListener("mousemove", handleMouseMove, false);
        oCan1.addEventListener("mouseup", handleMouseUp, false);
      };
      const bindEvent = () => {
        // 文件更改绑定监听事件
        oImage.addEventListener("change", handleFileChange, false);
        // 监听canvas区域内的按下事件
        oCan1.addEventListener("mousedown", handleMouseDown, false);
      };
      const init = () => {
        bindEvent();
      };
      init();
    </script>
  </body>
</html>
